package ierr

import (
	"errors"
	"fmt"
	"io"
)

type Error struct {
	*errStacks
	rawErr error
	code   int
}

func NewError(code int, err error, Option ...OptionFunc) IError {
	s := newStacks(Option...)
	s.resolve()

	return &Error{
		errStacks: s,
		rawErr:    err,
		code:      code,
	}
}

func NewErrorWithMsg(code int, msg string, Option ...OptionFunc) IError {
	err := errors.New(msg)
	return NewError(code, err, Option...)
}

func (e Error) Error() string {
	return fmt.Sprintf("[%d]%s", e.code, e.rawErr.Error())
}

func (e Error) Code() int {
	return e.code
}

func (e Error) Msg() string {
	return e.rawErr.Error()
}

func (e Error) Stacks() Stacks {
	return e.errStacks.stacks
}

func (e Error) formatString(s fmt.State, start, end, split string, full bool) {
	io.WriteString(s, e.Error()+start)
	e.stacks.formatString(s, split, full)
	io.WriteString(s, end)
}

func (e Error) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		switch {
		case s.Flag('+'):
			e.formatString(s, " track => [\n", "\n]", " \n ", false)
		case s.Flag('#'):
			e.formatString(s, " track => [\n", "\n]", " \n ", true)
		default:
			e.formatString(s, " track => ", "", " => ", false)
		}
	case 's':
		io.WriteString(s, e.Error())
	}
}
